home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / pgp20src.zip / ARMOR.C < prev    next >
C/C++ Source or Header  |  1992-09-03  |  21KB  |  795 lines

  1. /*    armor.c  - ASCII/binary encoding/decoding based partly on PEM RFC1113.
  2.     PGP: Pretty Good(tm) Privacy - public key cryptography for the masses.
  3.  
  4.     (c) Copyright 1990-1992 by Philip Zimmermann.  All rights reserved.
  5.     The author assumes no liability for damages resulting from the use
  6.     of this software, even if the damage results from defects in this
  7.     software.  No warranty is expressed or implied.
  8.  
  9.     All the source code Philip Zimmermann wrote for PGP is available for
  10.     free under the "Copyleft" General Public License from the Free
  11.     Software Foundation.  A copy of that license agreement is included in
  12.     the source release package of PGP.  Code developed by others for PGP
  13.     is also freely available.  Other code that has been incorporated into
  14.     PGP from other sources was either originally published in the public
  15.     domain or was used with permission from the various authors.  See the
  16.     PGP User's Guide for more complete information about licensing,
  17.     patent restrictions on certain algorithms, trademarks, copyrights,
  18.     and export controls.  
  19. */
  20.  
  21. #include <ctype.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include "mpilib.h"
  25. #include "fileio.h"
  26. #include "mpiio.h"
  27. #include "language.h"
  28. #include "pgp.h"
  29.  
  30. /*     Begin PEM routines.
  31.     This converts a binary file into printable ASCII characters, in a
  32.     radix-64 form mostly compatible with the PEM RFC1113 format.
  33.     This makes it easier to send encrypted files over a 7-bit channel.
  34. */
  35.  
  36. /* Index this array by a 6 bit value to get the character corresponding
  37.  * to that value.
  38.  */
  39. unsigned char bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
  40. abcdefghijklmnopqrstuvwxyz0123456789+/";
  41.  
  42. /* Index this array by a 7 bit value to get the 6-bit binary field
  43.  * corresponding to that value.  Any illegal characters return high bit set.
  44.  */
  45. unsigned char asctobin[] = {
  46.     0200,0200,0200,0200,0200,0200,0200,0200,
  47.     0200,0200,0200,0200,0200,0200,0200,0200,
  48.     0200,0200,0200,0200,0200,0200,0200,0200,
  49.     0200,0200,0200,0200,0200,0200,0200,0200,
  50.     0200,0200,0200,0200,0200,0200,0200,0200,
  51.     0200,0200,0200,0076,0200,0200,0200,0077,
  52.     0064,0065,0066,0067,0070,0071,0072,0073,
  53.     0074,0075,0200,0200,0200,0200,0200,0200,
  54.     0200,0000,0001,0002,0003,0004,0005,0006,
  55.     0007,0010,0011,0012,0013,0014,0015,0016,
  56.     0017,0020,0021,0022,0023,0024,0025,0026,
  57.     0027,0030,0031,0200,0200,0200,0200,0200,
  58.     0200,0032,0033,0034,0035,0036,0037,0040,
  59.     0041,0042,0043,0044,0045,0046,0047,0050,
  60.     0051,0052,0053,0054,0055,0056,0057,0060,
  61.     0061,0062,0063,0200,0200,0200,0200,0200
  62. };
  63. static long    infile_line;        /* Current line number for mult decodes */
  64.  
  65. /************************************************************************/
  66.  
  67. /* CRC Routines. */
  68. /*    These CRC functions are derived from code in chapter 19 of the book 
  69.     "C Programmer's Guide to Serial Communications", by Joe Campbell.
  70.     Generalized to any CRC width by Philip Zimmermann.
  71. */
  72.  
  73. #define byte unsigned char
  74.  
  75. #define CRCBITS 24    /* may be 16, 24, or 32 */
  76. /* #define crcword unsigned short */    /* if CRCBITS is 16 */
  77. #define crcword unsigned long        /* if CRCBITS is 24 or 32 */
  78. /* #define maskcrc(crc) ((crcword)(crc)) */    /* if CRCBITS is 16 or 32 */
  79. #define maskcrc(crc) ((crc) & 0xffffffL)    /* if CRCBITS is 24 */
  80. #define CRCHIBIT ((crcword) (1L<<(CRCBITS-1))) /* 0x8000 if CRCBITS is 16 */
  81. #define CRCSHIFTS (CRCBITS-8)
  82.  
  83. /*    Notes on making a good 24-bit CRC--
  84.     The primitive irreducible polynomial of degree 23 over GF(2),
  85.     040435651 (octal), comes from Appendix C of "Error Correcting Codes,
  86.     2nd edition" by Peterson and Weldon, page 490.  This polynomial was
  87.     chosen for its uniform density of ones and zeros, which has better
  88.     error detection properties than polynomials with a minimal number of
  89.     nonzero terms.  Multiplying this primitive degree-23 polynomial by
  90.     the polynomial x+1 yields the additional property of detecting any
  91.     odd number of bits in error, which means it adds parity.  This 
  92.     approach was recommended by Neal Glover.
  93.  
  94.     To multiply the polynomial 040435651 by x+1, shift it left 1 bit and
  95.     bitwise add (xor) the unshifted version back in.  Dropping the unused 
  96.     upper bit (bit 24) produces a CRC-24 generator bitmask of 041446373 
  97.     octal, or 0x864cfb hex.  
  98.  
  99.     You can detect spurious leading zeros or framing errors in the 
  100.     message by initializing the CRC accumulator to some agreed-upon 
  101.     nonzero "random-like" value, but this is a bit nonstandard.  
  102. */
  103.  
  104. #define CCITTCRC 0x1021 /* CCITT's 16-bit CRC generator polynomial */
  105. #define PRZCRC 0x864cfbL /* PRZ's 24-bit CRC generator polynomial */
  106. #define CRCINIT 0xB704CEL    /* Init value for CRC accumulator */
  107.  
  108. crcword crctable[256];        /* Table for speeding up CRC's */
  109.  
  110. /*    crchware simulates CRC hardware circuit.  Generates true CRC
  111.     directly, without requiring extra NULL bytes to be appended 
  112.     to the message.
  113.     Returns new updated CRC accumulator.
  114. */
  115. crcword crchware(byte ch, crcword poly, crcword accum)
  116. {    int i;
  117.     crcword data;
  118.     data = ch;
  119.     data <<= CRCSHIFTS;    /* shift data to line up with MSB of accum */
  120.     i = 8;        /* counts 8 bits of data */
  121.     do
  122.     {    /* if MSB of (data XOR accum) is TRUE, shift and subtract poly */
  123.         if ((data ^ accum) & CRCHIBIT)
  124.             accum = (accum<<1) ^ poly;
  125.         else
  126.             accum <<= 1;
  127.         data <<= 1;
  128.     } while (--i);    /* counts 8 bits of data */
  129.     return (maskcrc(accum));
  130. }    /* crchware */
  131.  
  132.  
  133. /*    mk_crctbl derives a CRC lookup table from the CRC polynomial.
  134.     The table is used later by crcupdate function given below.
  135.     mk_crctbl only needs to be called once at the dawn of time.
  136. */
  137. void mk_crctbl(crcword poly)
  138. {    int i;
  139.     for (i=0; i<256; i++)
  140.         crctable[i] = crchware((byte) i, poly, 0);
  141. }    /* mk_crctbl */
  142.  
  143.  
  144. /*    crcupdate calculates a CRC using the fast table-lookup method.
  145.     Returns new updated CRC accumulator.
  146. */
  147. crcword crcupdate(byte data, register crcword accum)
  148. {    byte combined_value;
  149.  
  150.     /* XOR the MSByte of the accum with the data byte */
  151.     combined_value = (accum >> CRCSHIFTS) ^ data;
  152.     accum = (accum << 8) ^ crctable[combined_value];
  153.     return (maskcrc(accum));
  154. }    /* crcupdate */
  155.  
  156. /* Initialize the CRC table using our codes */
  157. void init_crc()
  158. {    mk_crctbl(PRZCRC);
  159. }
  160.  
  161.  
  162. /************************************************************************/
  163.  
  164.  
  165. /* ENC is the basic 1 character encoding function to make a char printing */
  166. #define ENC(c) ((int)bintoasc[((c) & 077)])
  167. #define PAD        '='
  168.  
  169. /*
  170.  * output one group of up to 3 bytes, pointed at by p, on file f.
  171.  * if fewer than 3 are present, the 1 or two extras must be zeros.
  172.  */
  173. static void outdec(char *p, FILE *f, int count)
  174. {
  175.     int c1, c2, c3, c4;
  176.  
  177.     c1 = *p >> 2;
  178.     c2 = ((*p << 4) & 060) | ((p[1] >> 4) & 017);
  179.     c3 = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03);
  180.     c4 = p[2] & 077;
  181.     putc(ENC(c1), f);
  182.     putc(ENC(c2), f);
  183.     if (count == 1)
  184.     {    putc(PAD, f);
  185.         putc(PAD, f);
  186.     }
  187.     else
  188.     {    putc(ENC(c3), f);
  189.         if (count == 2)
  190.             putc(PAD, f);
  191.         else
  192.             putc(ENC(c4), f);
  193.     }
  194. }    /* outdec */
  195.  
  196.  
  197. /* Output the CRC value, MSB first per normal CRC conventions */
  198. static void outcrc (word32 crc, FILE *outFile)
  199. {    /* Output crc */
  200.     char crcbuf[4];
  201.     crcbuf[0] = (crc>>16) & 0xff;
  202.     crcbuf[1] = (crc>>8) & 0xff;
  203.     crcbuf[2] = (crc>>0) & 0xff;
  204.     putc(PAD,outFile);
  205.     outdec (crcbuf,outFile,3);
  206.     putc('\n',outFile);
  207. }    /* outcrc */
  208.  
  209. /* Return filename for output (text mode), but replace last letter of
  210.  * filename with the ascii for num (last two letters if num > 10).
  211.  */
  212. static char *numFilename( char *fname, int num)
  213. {    static char    fnamenum[MAX_PATH];
  214.     int        len;
  215.  
  216.     strcpy (fnamenum, fname);
  217.     len = strlen (fnamenum);
  218.     if (num < 10)
  219.         fnamenum[len-1] = '0' + num;
  220.     else    /* If num > 100, this will be slightly screwy */
  221.     {    fnamenum[len-2] = '0' + (num / 10);
  222.         fnamenum[len-1] = '0' + (num % 10);
  223.     }
  224.     return(fnamenum);
  225. }
  226.  
  227. /*    Encode a file in sections.  64 ASCII bytes * 720 lines = 46K, 
  228.     recommended max.  Usenet message size is 50K so this leaves a nice 
  229.     margin for .signature.  In the interests of orthogonality and 
  230.     programmer laziness no check is made for a message containing only 
  231.     a few lines (or even just an 'end')    after a section break. 
  232. */
  233. #define LINE_LEN    48L
  234. int pem_lines = 720;
  235. #define BYTES_PER_SECTION    (LINE_LEN * pem_lines)
  236.  
  237. extern boolean verbose;    /* Undocumented command mode in PGP.C */
  238. extern boolean filter_mode;
  239.  
  240. /*
  241.  * Copy from infilename to outfilename, PEM encoding as you go along,
  242.  * and with breaks every
  243.  * pem_lines lines.
  244.  */
  245. int pem_file(char *infilename, char *outfilename)
  246. {
  247.     char buffer[80];
  248.     int i,bytesRead,lines = 0;
  249.     int noSections, currentSection = 1;
  250.     long fileLen;
  251.     crcword crc;
  252.     FILE *inFile, *outFile;
  253.     char *blocktype = "MESSAGE";
  254.  
  255.     if (verbose)
  256.         fprintf(pgpout,PSTR("Converting output to ASCII armor format.\n"));
  257.  
  258.     /* open input file as binary */
  259.     if ((inFile = fopen(infilename,"rb")) == NULL)
  260.     {   
  261.         return(1);
  262.     }
  263.  
  264.     if (filter_mode || pem_lines == 0)
  265.         noSections = 1;
  266.     else
  267.     {    /* Evaluate how many parts this file will comprise */
  268.         fseek(inFile,0L,SEEK_END);
  269.         fileLen = ftell(inFile);
  270.         rewind(inFile);
  271.         noSections = (fileLen + BYTES_PER_SECTION - 1) / BYTES_PER_SECTION;
  272.     }
  273.     
  274.     if (noSections > 1)
  275.     {    force_extension(outfilename, ASC_EXTENSION);
  276.         outFile = fopen (numFilename (outfilename, 1), "w");
  277.     }
  278.     else
  279.         outFile = fopen(outfilename,"w");
  280.  
  281.     if (outFile == NULL)
  282.     {    fclose(inFile);
  283.         return(1);
  284.     }
  285.  
  286.     if (noSections == 1)
  287.     {
  288.         byte ctb = 0;
  289.         ctb = getc(inFile);
  290.         if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE))
  291.             blocktype = "PUBLIC KEY BLOCK";
  292.         fprintf (outFile, "-----BEGIN PGP %s-----\n",blocktype);
  293.         rewind(inFile);
  294.     }
  295.     else
  296.         fprintf (outFile, "-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n",
  297.                     1, noSections);
  298.     fprintf (outFile, "Version: %s\n",rel_version);
  299.     fprintf (outFile, "\n");
  300.  
  301.     init_crc();
  302.     crc = CRCINIT;
  303.  
  304.     while((bytesRead = fread(buffer,1,LINE_LEN,inFile)) > 0)
  305.     {    /* Munge up LINE_LEN characters */
  306.         if (bytesRead < LINE_LEN)
  307.             fill0 (buffer+bytesRead, LINE_LEN-bytesRead);
  308.  
  309.         for (i=0; i<bytesRead-2; i+=3) {
  310.             crc = crcupdate(buffer[i],crc);
  311.             crc = crcupdate(buffer[i+1],crc);
  312.             crc = crcupdate(buffer[i+2],crc);
  313.             outdec(buffer+i,outFile,3);
  314.         }
  315.  
  316.         if (i<bytesRead) {
  317.             outdec(buffer+i,outFile,bytesRead-i);
  318.             while (i<bytesRead)
  319.                 crc = crcupdate(buffer[i++],crc);
  320.         }
  321.         putc('\n',outFile);
  322.  
  323.         if (++lines == pem_lines && !filter_mode && currentSection < noSections)
  324.         {    lines = 0;
  325.             outcrc (crc, outFile);
  326.             fprintf(outFile,"-----END PGP MESSAGE, PART %02d/%02d-----\n\n",
  327.                 currentSection, noSections);
  328.             fclose (outFile);
  329.             outFile = fopen (numFilename (outfilename, ++currentSection), "w");
  330.             fprintf(outFile,"-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n",
  331.                     currentSection, noSections);
  332.             fprintf(outFile,"\n");
  333.             crc = CRCINIT;
  334.         }
  335.     }
  336.     outcrc (crc, outFile);
  337.  
  338.     if (noSections == 1)
  339.         fprintf (outFile, "-----END PGP %s-----\n\n",blocktype);
  340.     else
  341.         fprintf(outFile,"-----END PGP MESSAGE, PART %02d/%02d-----\n\n",
  342.                 noSections, noSections);
  343.  
  344.     /* Done */
  345.     if (ferror(outFile) || fclose(outFile))
  346.         return(-1);
  347.     fclose(inFile);
  348.  
  349.     if (filter_mode)
  350.         return(0);
  351.  
  352.     if (noSections == 1)
  353.         fprintf (pgpout, PSTR("\nTransport armor file: %s\n"), outfilename);
  354.     else
  355.     {    int i;
  356.         fprintf (pgpout, PSTR("\nTransport armor files: "));
  357.         for (i=1; i<=noSections; ++i)
  358.             fprintf (pgpout, "%s%s", numFilename(outfilename,i),
  359.                         i==noSections?"\n":", ");
  360.     }
  361.     return(0);
  362. }    /* pem_file */
  363.  
  364.  
  365.  
  366.  
  367.  
  368. /*     End PEM encode routines. */
  369.  
  370. /*    PEM decode routines.
  371. */
  372.  
  373. #define MAX_RETRY_LINES    100
  374.  
  375. /* Strip trailing spaces and CR characters */
  376. void
  377. strip_trailing (char *buf)
  378. {
  379.     char *bp = buf;
  380.     while (*bp++ != '\n')        /* Find end of line */
  381.         ;
  382.     --bp; --bp;                    /* And back up... */
  383.                                 /* Back up over spaces & CR */
  384.     while (bp >= buf  &&  (*bp == ' '  ||  *bp == '\r'))
  385.         --bp;
  386.     bp[1] = '\n';                /* Terminate line cleanly */
  387.     bp[2] = '\0';
  388. }
  389.  
  390.  
  391. int dpem_buffer(char *inbuf, char *outbuf, int *outlength)
  392. {
  393.     unsigned char *bp;
  394.     int    length;
  395.     unsigned int c1,c2,c3,c4;
  396.     register int j;
  397.  
  398.     length = 0;
  399.     bp = (unsigned char *)inbuf;
  400.  
  401.     /* FOUR input characters go into each THREE output charcters */
  402.  
  403.     while (*bp!='\0' && *bp!='\n' && *bp!='\r')
  404.     {    if (*bp&0x80 || (c1=asctobin[*bp])&0x80)
  405.             return -1;
  406.         ++bp;
  407.         if (*bp&0x80 || (c2=asctobin[*bp])&0x80)
  408.             return -1;
  409.         if (*++bp == PAD)
  410.         {    c3 = c4 = 0;
  411.             length += 1;
  412.             if (*++bp != PAD)
  413.                 return -1;
  414.         }
  415.         else if (*bp&0x80 || (c3=asctobin[*bp])&0x80)
  416.                 return -1;
  417.         else
  418.         {    if (*++bp == PAD)
  419.             {    c4 = 0;
  420.                 length += 2;
  421.             }
  422.             else if (*bp&0x80 || (c4=asctobin[*bp])&0x80)
  423.                 return -1;
  424.             else
  425.                 length += 3;
  426.         }
  427.         ++bp;
  428.         j = (c1 << 2) | (c2 >> 4);
  429.         *outbuf++=j;
  430.         j = (c2 << 4) | (c3 >> 2);
  431.         *outbuf++=j;
  432.         j = (c3 << 6) | c4;
  433.         *outbuf++=j;
  434.     }
  435.  
  436.     *outlength = length;
  437.     return(0);    /* normal return */
  438.  
  439. }    /* dpem_buffer */
  440.  
  441. static char pemfilename[MAX_PATH];
  442. /*
  443.  * try to open the next file of a multi-part armored file
  444.  * the sequence number is expected at the end of the file name
  445.  */
  446. static FILE *
  447. open_next()
  448. {
  449.     char *p, *s, c;
  450.     FILE *fp;
  451.  
  452.     p = pemfilename + strlen(pemfilename);
  453.     while (--p >= pemfilename && isdigit(*p))
  454.     {
  455.         if (*p != '9')
  456.         {
  457.             ++*p;
  458.             return(fopen(pemfilename, "r"));
  459.         }
  460.         *p = '0';
  461.     }
  462.  
  463.     /* need an extra digit */
  464.     if (p >= pemfilename)
  465.     {    /* try replacing character ( .as0 -> .a10 ) */
  466.         c = *p;
  467.         *p = '1';
  468.         if ((fp = fopen(pemfilename, "r")) != NULL)
  469.             return(fp);
  470.         *p = c;    /* restore original character */
  471.     }
  472.     ++p;
  473.     for (s = p + strlen(p); s >= p; --s)
  474.         s[1] = *s;
  475.     *p = '1'; /* insert digit ( fn0 -> fn10 ) */
  476.  
  477.     return(fopen(pemfilename, "r"));
  478. }
  479.  
  480. /*
  481.  * Copy from in to out, decoding as you go, with handling for multiple
  482.  * 500-line blocks of encoded data.
  483.  */
  484. int pemdecode(FILE *in, FILE *out)
  485. {
  486. char inbuf[96];
  487. char outbuf[64];
  488.  
  489. int i, n, status;
  490. int line;
  491. int lineCount = 0;
  492. int section, currentSection = 1;
  493. int noSections = 0;
  494. int gotcrc = 0;
  495. long crc=CRCINIT, chkcrc;
  496. char crcbuf[4];
  497.  
  498.     init_crc();
  499.  
  500.     for (line = 1; ; line++)    /* for each input line */
  501.     {
  502.         if (fgets(inbuf, sizeof(inbuf), in) == NULL)
  503.         {
  504.             fprintf(pgpout,PSTR("ERROR: ASCII armor decode input ended unexpectedly!\n"));
  505.             return(18);
  506.         }
  507.         ++infile_line;
  508.  
  509.         if (currentSection!=noSections &&
  510.                 strncmp(inbuf,"-----END PGP MESSAGE,", 21) == 0)
  511.         {    /* End of this section */
  512.             if (gotcrc)
  513.             {    if (chkcrc != crc)
  514.                 {    fprintf(pgpout,PSTR("ERROR: Bad ASCII armor checksum in section %d.\n"), currentSection);
  515.                     return -1;
  516.                 }
  517.             }
  518.             gotcrc = 0;
  519.             crc = CRCINIT;
  520.             lineCount = 0;
  521.             section = 0;
  522.  
  523.             /* Try and find start of next section */
  524.             do
  525.             {    if (fgets(inbuf,sizeof(inbuf),in) == NULL || ++lineCount == MAX_RETRY_LINES)
  526.                 {    FILE *nextf;
  527.                     if ((nextf = open_next()) != NULL)
  528.                     {
  529.                         fclose(in);
  530.                         in = nextf;
  531.                         lineCount = 0;
  532.                         continue;
  533.                     }
  534.                     fprintf(pgpout,PSTR("Can't find section %d.\n"),currentSection + 1);
  535.                     return(-1);
  536.                 }
  537.                 ++infile_line;
  538.             }
  539.             while (strncmp(inbuf,"-----BEGIN PGP MESSAGE",22));
  540.  
  541.             /* Make sure this section is the correct one */
  542.             lineCount = 0;
  543.             if (2 != sscanf(inbuf,"-----BEGIN PGP MESSAGE, PART %d/%d",
  544.                 §ion,&noSections))
  545.             {    fprintf(pgpout,PSTR("Badly formed section header, part %d.\n"),
  546.                     currentSection+1);
  547.                 return(-1);
  548.             }
  549.             if (section != ++currentSection)
  550.             {    fprintf(pgpout,PSTR("Sections out of order, expected part %d"),currentSection);
  551.                 if (section)
  552.                     fprintf(pgpout,PSTR(", got part %d\n"),section);
  553.                 else
  554.                     fputc('\n',pgpout);
  555.                 return(-1);
  556.             }
  557.  
  558.             /* Skip header after BEGIN line */
  559.             do {
  560.                 ++infile_line;
  561.                 if (fgets(inbuf, sizeof inbuf, in) == NULL)
  562.                 {
  563.                     fprintf(pgpout,PSTR("ERROR: Hit EOF in header of section %d.\n"),
  564.                         currentSection);
  565.                     return(-1);
  566.                 }
  567.             } while (inbuf[0] != '\n' && inbuf[0] != '\r');
  568.  
  569.             /* Continue decoding */
  570.             continue;
  571.         }
  572.  
  573.         /* Quit when hit the -----END PGP MESSAGE----- line or a blank,
  574.             or handle checksum */
  575.         if (inbuf[0] == PAD)    /* Checksum lines start with PAD char */
  576.         {    status = dpem_buffer (inbuf+1,crcbuf,&n);
  577.             if (status==-1  ||  n!=3)
  578.             {    fprintf(pgpout,PSTR("ERROR: Badly formed ASCII armor checksum, line %d.\n"),line);
  579.                 return -1;
  580.             }
  581.             chkcrc = (((long)crcbuf[0]<<16)&0xff0000L) +
  582.                 ((crcbuf[1]<<8)&0xff00L) + (crcbuf[2]&0xffL);
  583.             gotcrc = 1;
  584.             continue;
  585.         }
  586.         if (inbuf[0] == '\n'  ||  inbuf[0] == '\r')
  587.         {    fprintf(pgpout,PSTR("WARNING: No ASCII armor `END' line.\n"));
  588.             break;
  589.         }
  590.         if (strncmp(inbuf, "-----END PGP ", 13) == 0)
  591.             break;
  592.  
  593.         status = dpem_buffer(inbuf,outbuf,&n);
  594.  
  595.         if (status == -1)
  596.         {    fprintf(pgpout,PSTR("ERROR: Bad ASCII armor character, line %d.\n"), line);
  597.             return -1;
  598.         }
  599.  
  600.         if (n > sizeof outbuf)
  601.         {    fprintf(pgpout,PSTR("ERROR: Bad ASCII armor line length %d on line %d.\n"),
  602.                     n, line);
  603.             return -1;
  604.         }
  605.  
  606.         for (i=0; i<n; ++i)
  607.             crc = crcupdate(outbuf[i],crc);
  608.         fwrite(outbuf,1,n,out);
  609.  
  610.     }    /* line */
  611.  
  612.     if (gotcrc)
  613.     {    if (chkcrc != crc)
  614.         {    fprintf(pgpout,PSTR("ERROR: Bad ASCII armor checksum"));
  615.             if (noSections > 0)
  616.                 fprintf(pgpout,PSTR(" in section %d"), noSections);
  617.             fputc('\n',pgpout);
  618.             return -1;
  619.         }
  620.     }
  621.     else
  622.         fprintf(pgpout,PSTR("Warning: Transport armor lacks a checksum.\n"));
  623.  
  624.     return(0);    /* normal return */
  625. }   /* pemdecode */
  626.  
  627.  
  628. boolean is_pemfile(char *infile)
  629. {
  630.     FILE    *in;
  631.     char    inbuf[80];
  632.     char    outbuf[80];
  633.     int        i, n, status;
  634.     long    il;
  635.  
  636.     if ((in = fopen(infile, "r")) == NULL)
  637.     {    /* can't open file */
  638.         return(FALSE);
  639.     }
  640.  
  641.     /* Read to infile_line before we begin looking */
  642.     for (il=0; il<infile_line; ++il)
  643.     {
  644.         if (fgets(inbuf, sizeof inbuf, in) == NULL)
  645.             return(FALSE);
  646.     }
  647.  
  648.     /* search file for header line */
  649.     for (i=0; i<200; i++)    /* give up after 200 lines of garbage */
  650.     {
  651.         if (fgets(inbuf, sizeof inbuf, in) == NULL)
  652.             break;
  653.         else
  654.         {
  655.             if (strncmp(inbuf, "-----BEGIN PGP ", 15) == 0)
  656.             {
  657.                 do {
  658.                     if (fgets(inbuf, sizeof inbuf, in) == NULL)
  659.                         break;
  660. #ifndef STRICT_PEM
  661.                     if (dpem_buffer(inbuf,outbuf,&n) == 0 && n == 48)
  662.                     {    fclose(in);
  663.                         return(TRUE);
  664.                     }
  665. #endif
  666.                     strip_trailing(inbuf);
  667.                 } while (inbuf[0] != '\n');
  668.                 if (fgets(inbuf, sizeof inbuf, in) == NULL)
  669.                     break;
  670.                 status = dpem_buffer(inbuf,outbuf,&n);
  671.                 if (status < 0)
  672.                     break;
  673.                 fclose(in);
  674.                 return(TRUE);
  675.             }
  676.         }
  677.     }
  678.  
  679.     fclose(in);
  680.     return(FALSE);
  681.  
  682. }    /* is_pemfile */
  683.  
  684.  
  685. int dpem_file(char *infile, char *outfile, boolean *newname)
  686. {
  687. FILE    *in, *out;
  688. char    buf[80];
  689. char    outbuf[80];
  690. int        status, n;
  691. long    il, fpos;
  692.  
  693.     /* open PEM decode file as text */
  694.     if ((in = fopen(infile, "r")) == NULL)
  695.     {
  696.         fprintf(pgpout,PSTR("ERROR: Can't find file %s\n"), infile);
  697.         return(10);
  698.     }
  699.     strcpy(pemfilename, infile);    /* store filename for multi-parts */
  700.  
  701.     /* Skip to infile_line */
  702.     for (il=0; il<infile_line; ++il)
  703.     {
  704.         if (fgets(buf, sizeof buf, in) == NULL)
  705.             return(FALSE);
  706.     }
  707.  
  708.     /* Loop through file, searching for header.  Decode anything with a
  709.        header, complain if there were no headers. */
  710.  
  711.     /* search file for header line */
  712.     for (;;)
  713.     {
  714.         ++infile_line;
  715.         if (fgets(buf, sizeof buf, in) == NULL)
  716.         {
  717.             fprintf(pgpout,PSTR("ERROR: No ASCII armor `BEGIN' line!\n"));
  718.             fclose(in);
  719.             return(12);
  720.         }
  721.         if (strncmp(buf, "-----BEGIN PGP ", 15) == 0)
  722.             break;
  723.     }
  724.     /* Skip header after BEGIN line */
  725.     do {
  726.         ++infile_line;
  727.         fpos = ftell(in);
  728.         if (fgets(buf, sizeof buf, in) == NULL)
  729.         {
  730.             fprintf(pgpout,PSTR("ERROR: Hit EOF in header.\n"));
  731.             fclose(in);
  732.             return(13);
  733.         }
  734. #ifndef STRICT_PEM
  735.         if (dpem_buffer(buf,outbuf,&n) == 0 && n == 48)
  736.         {    fseek(in, fpos, SEEK_SET);
  737.             break;
  738.         }
  739. #endif
  740.         strip_trailing(buf);
  741.     } while (buf[0] != '\n');
  742.  
  743.     /* create output file as binary */
  744.     out = stdout; /* !NULL */
  745.     while (file_exists(outfile)  ||  ((out = fopen(outfile, "wb")) == NULL))
  746.     {    if (out != NULL)
  747.         {    fprintf(pgpout,PSTR("\n\007Ciphertext output file '%s' already exists.  Overwrite (y/N)? "), outfile);
  748.             if (getyesno('n'))
  749.             {    if ((out = fopen(outfile, "wb")) == NULL)
  750.                     fprintf(pgpout,PSTR("\n\007Unable to write ciphertext output file '%s'.\n"));
  751.                 else
  752.                     break;
  753.             }
  754.         }
  755.         else
  756.             fprintf(pgpout,PSTR("\n\007Unable to write ciphertext output file '%s'.\n"));
  757.         fprintf(pgpout, PSTR("Enter new name for ciphertext output file: "));
  758.         getstring( outfile, 255, TRUE );
  759.         if (*outfile == '\0')
  760.             return(-1);
  761.         *newname = TRUE;
  762.         out = stdout; /* !NULL */
  763.     }
  764.  
  765.     status = pemdecode(in, out);
  766.  
  767.     fflush(out);
  768.     if (ferror(out))
  769.         status = -1;
  770.     fclose(out);
  771.     fclose(in);
  772.     return(status);
  773. }   /* dpem_file */
  774.  
  775. /* Entry points for generic interface names */
  776. int
  777. armor_file (char *infile, char *outfile, char *filename)
  778. {
  779.     return pem_file (infile, outfile);
  780. }
  781.  
  782. int
  783. de_armor_file(char *infile, char *outfile, boolean *newname)
  784. {
  785.     *newname = FALSE;
  786.     return dpem_file (infile, outfile, newname);
  787. }
  788.  
  789. boolean
  790. is_armor_file (char *infile)
  791. {
  792.     return is_pemfile (infile);
  793. }
  794.  
  795.